#!/bin/bash -e
#   * compile instructions: put this file in
#   *         `steamapps/common/Dyson Sphere Program/CustomMod`
#   * folder, open a terminal in the same folder, and execute:
#   *
#   * ```
#   *     chmod +x ${file}.cs
#   *     ./${file}.cs
#   * ```
#   *
#   * then the mod will be compiled automatically.
#   *
#   * Here we wrote a shebang like file, which is correct
#   * in my computer (Manjaro XFCE), if such script do not work
#   * in your computer, you could just try the instructions below :

if [ -z "$__DOTNET_CSC" ]; then
    export __DOTNET_CSC="`find /usr/share/dotnet -type f -name dotnet` `find /usr/share/dotnet -name csc.dll`"
    echo '$'"__DOTNET_CSC not set yet, you should execute"
    echo "export __DOTNET_CSC='$__DOTNET_CSC'"
    echo "manually, or this alert will occur each time you execute this script."
fi

__MODE_VERBOSE=86 # is the line number of "#define VERBOSE", may be modified
__MODE_DEBUG__=$((__MODE_VERBOSE+1))
__MODE_RELEASE=$((__MODE_DEBUG__+1))

case $1 in
    V)       _MODE__SELECT_=$__MODE_VERBOSE     ;;
    v)       _MODE__SELECT_=$__MODE_VERBOSE     ;;
    VERBOSE) _MODE__SELECT_=$__MODE_VERBOSE     ;;
    verbose) _MODE__SELECT_=$__MODE_VERBOSE     ;;
    D)       _MODE__SELECT_=$__MODE_DEBUG__     ;;
    d)       _MODE__SELECT_=$__MODE_DEBUG__     ;;
    DEBUG)   _MODE__SELECT_=$__MODE_DEBUG__     ;;
    debug)   _MODE__SELECT_=$__MODE_DEBUG__     ;;
    *)       _MODE__SELECT_=$__MODE_RELEASE     ;;
esac

export GAME_NAME="${0%\.cs}" # now, file name should be equals to ${GAME_NAME}.cs, the benefit is, we could only change the name, rather than change filename and $GAME_NAME.
export GAME_NAME="The Scroll Of Taiwu Alpha V1.0"
export FILE_NAME="$0"
export ASSEMBLY="Assembly-CSharp"
export GAME_BASE_DIR="../common/The Scroll Of Taiwu"
export PLUGIN_ID="Neutron3529.Taiwu.Balance.Collection"
export NAMESPACE_ID="Neutron3529.Balance.Collection"

( yes "" | head -n $_MODE__SELECT_ | head -n-1  ; tail $FILE_NAME -n+$_MODE__SELECT_ ) | sed s/%%NAMESPACE_ID%%/${NAMESPACE_ID}/g | sed s/%%PLUGIN_ID%%/${PLUGIN_ID}/g | $__DOTNET_CSC -nologo -t:library \
  -r:"${GAME_BASE_DIR}/BepInEx/core/BepInEx.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/core/0Harmony.dll" \
  -r:"${GAME_BASE_DIR}/BepInEx/core/BepInEx.Harmony.dll" \
  `[ -e "${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/netstandard.dll" ] && echo "-r:\"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/netstandard.dll\""` \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/System.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/System.Core.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/UnityEngine.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/UnityEngine.UI.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/UnityEngine.CoreModule.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/mscorlib.dll" \
  -r:"${GAME_BASE_DIR}/${GAME_NAME}_Data/Managed/Assembly-CSharp-886238F0C737D5551CA4940C9724A53C94395A280021C721ED03A5241482CB97.dll" \
  -out:"${GAME_BASE_DIR}/BepInEx/plugins/${FILE_NAME%.*}".dll \
  -optimize \
  - && rm -f "${GAME_BASE_DIR}/BepInEx/config/${PLUGIN_ID}.cfg";

#  -r:"${GAME_BASE_DIR}/BepInEx/plugins/YanCore/YanLib.dll" \
#  -r:"${GAME_BASE_DIR}/BepInEx/plugins/YanCore/UnityUIKit.dll" \
#  -r:"${GAME_BASE_DIR}/BepInEx/plugins/YanCore/TaiwuUIKit.dll" \

if [ -n "$2" ]; then
    git add ${FILE_NAME}
    case $2 in
        R) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        r) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        RANDOM) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        random) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        U) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        u) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        UPLOAD) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        upload) git commit -am "`curl -s http://whatthecommit.com/index.txt`" ;;
        *) git commit -am "$2" ;;
    esac
    git push
fi
exit


#define VERBOSE // the line of __MODE_VERBOSE
#define DEBUG
//#define USE_UI


using System;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections.Generic;

using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.UI;

#if USE_UI

using TaiwuUIKit.GameObjects;
using UnityUIKit.Core;
using UnityUIKit.GameObjects;
using YanLib.ModHelper;

#endif

namespace %%NAMESPACE_ID%%
{


    [BepInPlugin("%%PLUGIN_ID%%", "%%NAMESPACE_ID%%", "0.1.5")]
    public class Balance : BaseUnityPlugin {
        private static Action<string> _logger=s=>{};//用于内联代码
        [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        public static void logger(string s,string extra_id=""){
#if DEBUG
            _logger(extra_id+":"+s);
#endif
        }

        public static KeyCode RepeatJumpKey=KeyCode.V;
        public static KeyCode TanTuiKey=KeyCode.X;

        void Awake() {
            // Harmony.DEBUG=true;
            var harmony=new Harmony("%%PLUGIN_ID%%");
#if DEBUG
            _logger=Logger.LogInfo;
            logger("开始注入");
#endif
            if((int)(RepeatJumpKey=(KeyCode)Config.Bind("config", "RepeatJumpKey", (int)RepeatJumpKey, "反复横跳快捷键，默认为V，设为0关闭反复横跳patch").Value)!=0){
                TanTuiKey=(KeyCode)Config.Bind("config", "TanTuiKey", (int)TanTuiKey, "反复横跳切换弹腿缩地的快捷键，默认为X").Value;
                logger("patch RepeatJump");
                harmony.PatchAll(typeof(BattleSystemUpdate));
                logger("RepeatJump patched");
            }
            if(Config.Bind("config", "Faster", true, "加速动画播放").Value){
                logger("patch Faster");
                harmony.PatchAll(typeof(Faster));
                if(Config.Bind("config", "Faster_Patch", true, "略微加速功法特效提示的消失，以防止其持续显示在屏幕上").Value){
                    harmony.PatchAll(typeof(Faster_Patch));
                }
                logger("Faster patched");
            }
            if(Config.Bind("config", "AudioManagerUpdatePlaceBGM", true, "修改地图BGM为特有BGM").Value){
                logger("patch AudioManagerUpdatePlaceBGM");
                harmony.PatchAll(typeof(AudioManagerUpdatePlaceBGM));
                logger("AudioManagerUpdatePlaceBGM patched");
            }
            logger("加载完成");
        }
        [HarmonyPatch(typeof(BattleSystem), "Update")]
        public static class BattleSystemUpdate {
            public static int distance_1st=20;
            public static int distance_2nd=90;
            public static bool distancenable=false;
            public static bool tantui=false;
            public static bool first_run=true;
            public static int from=0;
            public static int target=0;
            static bool Prefix(BattleSystem __instance){
//                 if(first_run){
//                     first_run=false;
//                     DateFile.instance.battleTypDate[2001][0]+="&反复横跳";
//                     DateFile.instance.battleTypDate[2001][99]+="\n由于打开了反复横跳Mod，这个齿轮将会成为反复横跳的指示器\n按<color=#63CED0FF>"+('A'+((int)RepeatJumpKey-(int)KeyCode.A))+"</color>切换反复横跳Mod的开启状态，按<color=#63CED0FF>"+('A'+((int)TanTuiKey-(int)KeyCode.A))+"</color>切换弹腿缩地开关\n在反复横跳开启时都会保证人物优先跳向目标位置，而当人物跳到目标位置时，人物会向开启反复横跳的位置进行横跳。\n如果弹腿缩地开关开启，每次人物横跳的距离不会超过0.5。";
//                     logger("弹腿说明注入完成。");
//                 }
                if(Input.GetKeyDown(RepeatJumpKey)){
                    distance_1st=__instance.actorNeedRange;
                    distance_2nd=__instance.battleRange;
                    distancenable=!distancenable;
                    from=0;
                }
                if(Input.GetKeyDown(TanTuiKey)){
                    tantui=!tantui;
                    from=0;
                }
                if(distancenable){
                    __instance.autoBattle.transform.Rotate(new Vector3(0f, 0f, 100f) * (tantui?-2*Time.deltaTime:Time.deltaTime));
                    if(!__instance.autoBattle.isOn && __instance.actorNeedRangeSlider.interactable){
                        if(from!=__instance.battleRange){
                            from=__instance.battleRange;
                            int to=from==distance_1st?distance_2nd:distance_1st;
                            target=(tantui&&(BattleVaule.instance.GetMoveSpeed(true, DateFile.instance.mianActorId, true, 0)<15000))?Mathf.Clamp(to,from-5,from+5):to;
                        }
                        __instance.SetNeedRange(true, target, false);
                    }
                }
                return true;
            }
        }
        [HarmonyPatch(typeof(AudioManager), "UpdatePlaceBGM")]
        public static class AudioManagerUpdatePlaceBGM {
            public static MethodInfo isBgmPlaying=typeof(AudioManager).GetMethod("isBgmPlaying",(BindingFlags)(-1));
            public static bool Prefix(AudioManager __instance,ref int partId, ref int placeId) {
                int worldId = DateFile.instance.mianWorldId;
//                logger("change BGM: worldId="+worldId+",partId="+partId+",placeId="+placeId);
                // AllWorldMap_Date 101,103,地格
                // 太吾村: worldId=2,partId=1031（？）,placeId=253（地格，不应该使用）

                int type;
                int num = int.Parse(DateFile.instance.GetNewMapDate(partId, placeId, 92));

                if(num==16 && partId<1000 && worldId<15){
                    type=(partId<100)?3:2;
                    num=worldId+type-2;
                }else{
                    type=(int.Parse(DateFile.instance.GetNewMapDate(partId, placeId, 89)) == 2) ? 2 : 3;
                }
//                logger("change BGM: worldId="+worldId+",partId="+partId+",placeId="+placeId+",type="+type+",curnum="+num);

                bool flag = num == -1;
                if (flag)
                {
                    __instance.StopAllBGM(3f);
                }
                else
                {
                    bool flag2 = !(bool)(isBgmPlaying.Invoke(__instance,new object[]{type, num}));
                    if (flag2)
                    {
                        __instance.PlayBGM(type, num, 3f, true);
                    }
                }
                return false;
            }
        }
        public static class Faster {
            static IEnumerable<MethodInfo> TargetMethods() {
                foreach(MethodInfo i in new MethodInfo[]{
                    typeof(BattleEndWindow).GetMethod("BattleEndDone",(BindingFlags)(-1)),
                    typeof(BattleEndWindow).GetMethod("ShowBattleEndWindow",(BindingFlags)(-1)),
                    typeof(BattleEndWindow).GetMethod("ShowBattleRated",(BindingFlags)(-1)),
                    typeof(BattleEndWindow).GetMethod("ShowBootyIcon",(BindingFlags)(-1)),
                    typeof(BattleEndWindow).GetMethod("ShowRatedIcon",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("StartBattle",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("BattleState",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("QuquBattleWin",(BindingFlags)(-1)),
                    typeof(ReadBook).GetMethod("CloseReadBook",(BindingFlags)(-1)),
                    typeof(ReadBook).GetMethod("MakeGetIntIcon",(BindingFlags)(-1)),
                    typeof(ReadBook).GetMethod("ShowReadBookWindow",(BindingFlags)(-1)),
                    typeof(ReadBook).GetMethod("StartReadBook",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("BattleEndWindow",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("EnemyAnAnswer",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("EnemyAnswer",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("EnemyChoose",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("EnemyReAnswer",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("EnemySetQusetion",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("LoseQusetionDone",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("SkillBattleWin",(BindingFlags)(-1)),
                    typeof(SkillBattleSystem).GetMethod("StartSkillBattle",(BindingFlags)(-1)),
                    typeof(StudyWindow).GetMethod("CloseStudy",(BindingFlags)(-1)),
                    typeof(StudyWindow).GetMethod("ShowStudyWindow",(BindingFlags)(-1)),
                    typeof(StudyWindow).GetMethod("StartStudy",(BindingFlags)(-1))
                }){
                    if(i==null){logger("FATAL:NULL i");}else{
                    logger("修改类别"+i.ToString()+"的MoveNext方法");
                    if(AccessTools.EnumeratorMoveNext(i)!=null){yield return AccessTools.EnumeratorMoveNext(i);}else{logger("error:i="+i.ToString()+", have no move next");}
                    }
                }
                foreach(MethodInfo i in new MethodInfo[]{
                    typeof(QuquBattleSystem).GetMethod("Damage",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("SetBattleStateText",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("QuquBattleLoopStart",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("QuquAttack",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("QuquBaseAttack",(BindingFlags)(-1)),
                    typeof(QuquBattleSystem).GetMethod("SetActorAnimation",(BindingFlags)(-1))
                }){
                    if(i==null){logger("FATAL:NULL i in QuquBattleSystem");}else{
                    logger("修改QuquBattleSystem的"+i.ToString()+"方法");
                    yield return i;
                    }
                }
            }
            static IEnumerable<CodeInstruction> Transpiler(MethodBase original,IEnumerable<CodeInstruction> instructions) {
                logger("加速Transpiler正在注入"+original.Name);
                ConstructorInfo waitForSeconds=typeof(UnityEngine.WaitForSeconds).GetConstructor(new Type[]{typeof(float)});
                foreach(var i in instructions){
                    if(
                        (i.opcode==OpCodes.Call && ((MethodInfo)i.operand).Name=="SetDelay")
                    ||  (i.opcode==OpCodes.Newobj && ((ConstructorInfo)i.operand)==waitForSeconds)
                        ){
                        if(i.opcode==OpCodes.Call)logger("找到SetDelay");else logger("找到waitForSeconds");
                        yield return new CodeInstruction(OpCodes.Ldc_R4,0.1f);
                        yield return new CodeInstruction(OpCodes.Mul,null);
                    }
                    yield return i;
                }
            }
        }
        [HarmonyPatch(typeof(BattleSystem), "HideBattleState",MethodType.Enumerator)]
        public static class Faster_Patch {
            static IEnumerable<CodeInstruction> Transpiler(MethodBase original,IEnumerable<CodeInstruction> instructions) {
                logger("加速Transpiler正在注入"+original.Name);
                ConstructorInfo waitForSeconds=typeof(UnityEngine.WaitForSeconds).GetConstructor(new Type[]{typeof(float)});
                foreach(var i in instructions){
                    if (i.opcode==OpCodes.Newobj && ((ConstructorInfo)i.operand)==waitForSeconds) {
                        logger("找到waitForSeconds");
                        yield return new CodeInstruction(OpCodes.Ldc_R4,0.3f);
                        yield return new CodeInstruction(OpCodes.Mul,null);
                    }
                    yield return i;
                }
            }
        }
    }
}

